/***
 *  Platypus: Page Layout and Typesetting Software (free at platypus.pz.org)
 *
 *  Platypus is (c) Copyright 2006-12 Pacific Data Works LLC. All Rights Reserved.
 *  Licensed under Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0.html)
 */

package org.pz.platypus.parser;

import org.pz.platypus.GDD;

import static com.google.common.base.Preconditions.*;

/**
 * Parses macros, looks up system macros, passes through user macros, and writes resulting tokens to token stream.
 * @author alb
 */
public class MacroParser
{
    private GDD gdd;

    public MacroParser( final GDD gdd )
    {
        this.gdd = checkNotNull( gdd );
    }

    /**
     * Principal function. Extracts the macro name and looks it up. It
     * dumps the expanded text into the expandedMacro parameter and returns
     * the number of chars parsed in the input text.
     * @param tokens list of tokens to which macro is added
     * @return  the number of chars to move the isLineComment point forward
     */
//    public int parse( char[] input, int parsePoint, final TokenList tokens )
    public int parse( ParseContext context, final TokenList tokens )
    {
        checkNotNull( context, gdd.getLit( "ERROR.INVALID_PARAM_IN_MACROPARSER.PARSE" ));
        checkNotNull( tokens, gdd.getLit( "ERROR.INVALID_PARAM_IN_MACROPARSER.PARSE" ));

        // syntactical checks...
        int parsePoint = context.startPoint;
        char[] input = context.chars;
        checkArgument( parsePoint >= 0, gdd.getLit( "ERROR.INVALID_PARAM_IN_MACROPARSER.PARSE" ));
        checkArgument( input[parsePoint] == '[' && input[parsePoint+1] == '$' ,
                       gdd.getLit( "ERROR.INVALID_PARAM_IN_MACROPARSER.PARSE" )) ;
        // macro must be at least 2 chars, so if parsePoint points to [,
        // then "$ab]"+ EOL is minimum that must be present
        checkArgument( parsePoint + 4 < input.length, gdd.getLit( "ERROR.INVALID_PARAM_IN_MACROPARSER.PARSE" ));

        int startParsePoint = parsePoint;
        String macro;
        String expandedMacro;

        try {
            macro = new MacroNameExtractor().getName( input, parsePoint+2, gdd );
        }
        catch ( IllegalArgumentException malformedMacro ) {
            parsePoint += malformedMacro.toString().length();
            return( parsePoint - startParsePoint );
        }

        if ( macro.startsWith( "_" )) {
            expandedMacro = lookupSystemMacro( macro );
            if( expandedMacro != null )
                tokens.add( new Token( context.source, TokenType.TEXT, expandedMacro ));
        }
        else
            tokens.add( new Token( context.source, TokenType.MACRO, macro ));

        return( parsePoint + 2 + macro.length() - startParsePoint + 1 ); // the +1 added below is for the end ]
    }

    /**
     * Look up a macro in the table of System stings
     * @param macro macro to look up
     * @return expanded macro from the table; null, if an error occurred
     */
    public String  lookupSystemMacro( final String macro )
    {
        String expandedMacro = gdd.sysStrings.get( macro );
        if ( expandedMacro == null ) {
            gdd.log.warning( gdd.getLit( "ERROR.MACRO_NOT_FOUND" ) + ": " + macro );
        }
        return( expandedMacro );
    }
}
